/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jpxx.workflow.util;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Vector;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.jpxx.workflow.Destination;
import org.jpxx.workflow.DirectedGraph;
import org.jpxx.workflow.Node;
import org.jpxx.workflow.NodeType;
import org.jpxx.workflow.WorkFlowException;
import org.jpxx.workflow.WorkflowConstant;

/**
 * 
 * @author John Lee
 * 
 */
public class WorkFlowGraphSerializer {

	private static final String template = "<"
			+ WorkflowConstant.WORKFLOW_NODE_NAME
			+ "><nodes></nodes><transitions></transitions></"
			+ WorkflowConstant.WORKFLOW_NODE_NAME + ">";

	public static String serialize(DirectedGraph dg) throws WorkFlowException {

		String graphName = dg.getName();
		String result = null;

		SAXReader saxReader = new SAXReader();
		try {
			InputStream is = new ByteArrayInputStream(template.getBytes());
			Document document = saxReader.read(is);
			Element rootElement = document.getRootElement();
			rootElement.addAttribute("name", graphName);

			Element nodes = rootElement.element("nodes");
			// Serialize nodes
			Vector<Node> gnodes = dg.getAllNodes();
			for (Node gnode : gnodes) {
				String nodeName = gnode.getNodeName();
				String nodeType = gnode.getNodeType();
				Element nodeEl = nodes.addElement("node");
				nodeEl.addAttribute("id", nodeName);
				nodeEl.addAttribute("type", nodeType);

				if (nodeType.equals(NodeType.CONTAINER.getType())) {
					String containee = gnode.getContainee();
					nodeEl.addAttribute("containee", containee);
					int containeeVersion = gnode.getContaineeVersion();
					if (containeeVersion != -1) {
						nodeEl.addAttribute("containeeVersion", ""
								+ containeeVersion);
					}
				}

				if (nodeType.equals(NodeType.PROCESS.getType())) {
					int timeoutMinutes = gnode.getTimeoutMinutes();
					if (timeoutMinutes != -1) {
						nodeEl.addAttribute("timeoutMinutes", ""
								+ timeoutMinutes);
					}
					String timeoutHandler = gnode.getTimeoutHandler();
					if (timeoutHandler != null) {
						nodeEl.addAttribute("timeoutHandler", timeoutHandler);
					}
				}
			}

			Element transitions = rootElement.element("transitions");
			Node rootNode = dg.getRootNode();
			serializeTransitions(document, transitions, rootNode);

			result = serialize(rootElement);

		} catch (Exception e) {
			throw new WorkFlowException(e.getMessage());
		}
		return result;
	}

	public static String serialize(Element element) throws WorkFlowException {

		String serialized = null;

		try {
			element.normalize();
			XMLWriter output = null;
			OutputFormat format = OutputFormat.createPrettyPrint();
			format.setEncoding("UTF-8");
			StringWriter sw = new StringWriter();
			output = new XMLWriter(sw, format);
			output.write(element);
			output.close();
			serialized = sw.toString();

		} catch (Exception e) {
			throw new WorkFlowException(e.getMessage());
		}

		return serialized;
	}

	private static void serializeTransitions(Document doc,
			Element transitionsEl, Node gnode) {

		Vector<Destination> destinations = gnode.getDestinations();
		for (int i = 0; i < destinations.size(); i++) {
			Destination d = (Destination) destinations.elementAt(i);
			Element transEl = transitionsEl.addElement("transition");

			transEl.addAttribute("from", gnode.getNodeName());
			transEl.addAttribute("to", d.getNode().getNodeName());
			if (d.getRule() != null) {
				Element ruleEl = transEl.addElement("rule");
				ruleEl.setText(d.getRule().toString());
			}
			serializeTransitions(doc, transitionsEl, d.getNode());
		}
	}
}
